﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Web;
using System.Web.Mvc;
using YYSService;

namespace System
{
    public static class v3AuthorizeExtentions
    {
        public static unifiedUser v3UnifiedUser(this HttpRequestBase Request)
        {
            return Request.RequestContext.RouteData.DataTokens["__unifiedUser"] as unifiedUser;
        }
        public static string v3JsCss(this HttpRequestBase Request, string key)
        {
            var ver = Xcode.API.xAppSettings("JsCss_Version");
            var path = Xcode.API.xAppSettings(key);
            var str = path + (!string.IsNullOrWhiteSpace(ver) ? "?" + ver : "");
            str = str.Replace("http://", "https://");
            return str;
        }
    }
}

namespace YYS
{
    public sealed class v3Authorize : AuthorizeAttribute
    {
        public v3Authorize()
        {
            this.reqType = ReqFromType.Default;
            this.accessId = null;
        }
        #region
        bool _antiForgery = true;
        /// <summary>
        /// 是否验证防伪标记
        /// </summary>
        public bool antiForgery { get { return _antiForgery; } set { _antiForgery = value; } }
        /// <summary>
        /// 请求来源
        /// <para>GET 时有效</para>
        /// </summary>
        public YYS.ReqFromType reqType { get; set; }
        /// <summary>
        /// 学校必须
        /// </summary>
        public bool schoolMust { get; set; }
        /// <summary>
        /// 验证学校权限
        /// </summary>
        public bool schoolAccess { get; set; }
        /// <summary>
        /// 验证权限ID
        /// <para>同时包含权限用 &amp; 分隔，包含任意权限用 | 分隔</para>
        /// </summary>
        public object accessId { get; set; }
        /// <summary>
        /// 教师权限ID
        /// <para>同时包含权限用 &amp; 分隔，包含任意权限用 | 分隔</para>
        /// </summary>
        public object accessTeacher { get; set; }
        /// <summary>
        /// 用户必须
        /// </summary>
        public bool userMust { get; set; }
        /// <summary>
        /// 检查浏览器支持
        /// </summary>
        public bool checkBrowser { get; set; }
        /// <summary>
        /// 指定用户类型
        /// <para>0：教师；1：家长；2：学生；3：管理员</para>
        /// <para>任意用户类型用 | 分隔</para>
        /// </summary>
        public object userType { get; set; }

        int _accessGet = 1;
        /// <summary>
        /// 管理员有效 获取权限列表 0：不获取；1：仅权限ID 2：完整权限
        /// </summary>
        public int accessGet { get { return _accessGet; } set { _accessGet = value; } }

        int _schoolGet = 1;
        /// <summary>
        /// 管理员有效 获取有权限的学校列表 0：不获取；1：仅学校ID 2：完整学校信息
        /// </summary>
        public int schoolGet { get { return _schoolGet; } set { _schoolGet = value; } }
        /// <summary>
        /// 是否获取用户明细
        /// </summary>
        public bool getUserDetail { get; set; }
        /// <summary>
        /// 是否获取学校安智云ID
        /// </summary>
        public bool getAnzhiYunId { get; set; }
        /// <summary>
        /// 是否获取前置发卡配置
        /// </summary>
        public bool getFrontCard { get; set; }
        /// <summary>
        /// 是否获取ABC应用
        /// </summary>
        public bool getABCMenu { get; set; }
        /// <summary>
        /// 是否微信绑定状态 getUserDetail = True 有效
        /// </summary>
        public bool getWxBind { get; set; }
        /// <summary>
        /// 是否教师任课关联 getUserDetail = True 有效
        /// </summary>
        public bool getTeacherClass { get; set; }
        /// <summary>
        /// 是否仅验证微信授权，公众号、企业号 GET请求时有效
        /// </summary>
        public bool onlyWxAuth { get; set; }
        #endregion
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
                var context = new v3Auth(filterContext, this.reqType, this.schoolMust, this.schoolAccess, this.accessId, this.accessTeacher, this.userMust
                    , this.checkBrowser, this.userType, this.accessGet, this.schoolGet, this.antiForgery
                    , this.getUserDetail, this.getAnzhiYunId, this.getFrontCard, this.getABCMenu, this.getWxBind, this.getTeacherClass, this.onlyWxAuth);
                context.OnAuthorization();
                context = null;
           
        }
    }

    public class v3Auth
    {
        #region        
        /// <summary>
        /// 是否验证防伪标记
        /// </summary>
        public bool antiForgery { get; private set; }
        /// <summary>
        /// 请求来源
        /// <para>POST 时有效</para>
        /// </summary>
        public ReqFromType reqType { get; private set; }
        /// <summary>
        /// 学校必须
        /// </summary>
        public bool schoolMust { get; private set; }
        /// <summary>
        /// 是否验证管理员学校权限
        /// </summary>
        public bool schoolAccess { get; private set; }
        /// <summary>
        /// 验证管理员权限ID
        /// <para>同时包含权限用 & 分隔，包含任意权限用 | 分隔</para>
        /// </summary>
        public object accessId { get; private set; }
        /// <summary>
        /// 教师权限ID
        /// <para>同时包含权限用 & 分隔，包含任意权限用 | 分隔</para>
        /// </summary>
        public object accessTeacher { get; private set; }
        /// <summary>
        /// 用户必须
        /// </summary>
        public bool userMust { get; private set; }
        /// <summary>
        /// 检查浏览器支持
        /// </summary>
        public bool checkBrowser { get; private set; }
        /// <summary>
        /// 指定用户类型
        /// <para>0：教师；1：家长；2：学生；3：管理员</para>
        /// <para>任意用户类型用 | 分隔</para>
        /// </summary>
        public object userType { get; private set; }
        /// <summary>
        /// 管理员有效 获取权限列表 0：不获取；1：仅权限ID 2：完整权限
        /// </summary>
        public int accessGet { get; private set; }
        /// <summary>
        /// 管理员有效 获取有权限的学校列表 0：不获取；1：仅学校ID 2：完整学校信息
        /// </summary>
        public int schoolGet { get; private set; }
        /// <summary>
        /// 是否获取用户明细
        /// </summary>
        public bool getUserDetail { get; private set; }
        /// <summary>
        /// 是否获取学校安智云ID
        /// </summary>
        public bool getAnzhiYunId { get; private set; }
        /// <summary>
        /// 是否获取前置发卡配置
        /// </summary>
        public bool getFrontCard { get; private set; }
        /// <summary>
        /// 是否获取ABC应用
        /// </summary>
        public bool getABCMenu { get; private set; }
        /// <summary>
        /// 是否微信绑定状态 getUserDetail = True 有效
        /// </summary>
        public bool getWxBind { get; private set; }
        /// <summary>
        /// 是否教师任课关联 getUserDetail = True 有效
        /// </summary>
        public bool getTeacherClass { get; private set; }
        /// <summary>
        /// 是否仅验证微信授权，公众号、企业号 GET请求时有效
        /// </summary>
        public bool onlyWxAuth { get; private set; }
        #endregion

        public AuthorizationContext AuthorizationContext { get; private set; }
        public string Key { get; private set; }
        public bool IsPost { get; private set; }
        public HttpContextBase HttpContext { get { return AuthorizationContext.HttpContext; } }
        public HttpRequestBase Request { get { return HttpContext.Request; } }

        public v3Auth(AuthorizationContext filterContext, YYS.ReqFromType reqType, bool schoolMust, bool schoolAccess, object accessId, object accessTeacher, bool userMust
            , bool checkBrowser, object userType, int accessGet, int schoolGet, bool antiForgery
            , bool getUserDetail, bool getAnzhiYunId, bool getFrontCard, bool getABCMenu, bool getWxBind, bool getTeacherClass, bool onlyWxAuth)
        {
            this.AuthorizationContext = filterContext;
            this.reqType = reqType;
            this.schoolMust = schoolMust;
            this.schoolAccess = schoolAccess;
            this.accessId = accessId;
            this.accessTeacher = accessTeacher;
            this.userMust = userMust;
            this.checkBrowser = checkBrowser;
            this.userType = userType;
            this.accessGet = accessGet;
            this.schoolGet = schoolGet;
            this.antiForgery = antiForgery;

            this.getUserDetail = getUserDetail;
            this.getAnzhiYunId = getAnzhiYunId;
            this.getFrontCard = getFrontCard;
            this.getABCMenu = getABCMenu;
            this.getWxBind = getWxBind;
            this.getTeacherClass = getTeacherClass;
            this.onlyWxAuth = onlyWxAuth;
            this.IsPost = Request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase);
        }

        public void NotValid(string errmsg, string title = "提示", string autoURL = null, int autoTimeout = 3 ,int errcode = -999)
        {
            if (this.IsPost)
            {
                bool go = errmsg.Contains("Authorization");
                if (!go)
                {
                    this.AuthorizationContext.Result = new JsonResult() { Data = new { code = 999, errmsg = errmsg } };
                    return;
                }
            }
            var config = v3Auth.HostConfig.getHost(this.Request);
            autoURL = config.getAbsoluteUrl(this.Request, "~/pc/Login");

            var model = new YYS.ResultModel() { IsPart = 1, Icon = 5, Title = title, Msg = errmsg, AutoTimeout = autoTimeout, AutoURL = autoURL };
            var controler = this.AuthorizationContext.Controller as YYS.Controllers.ControlerEx;
            string errUrl = controler.getResultUri(model, Xcode.API.xGuid());
            this.AuthorizationContext.Result = new JsonResult() { Data = new { code = errcode, errmsg = errmsg, data = new { url = errUrl } } };
            //this.AuthorizationContext.Result = new System.Web.Mvc.RedirectResult(errUrl);

        }

        #region 域名配置
        public class HostConfig
        {
            public string reqHost { get; private set; }
            public string reqUrl { get; private set; }
            public string rawHost { get; private set; }
            public string rawUrl { get; private set; }
            public int appId { get; private set; }

            public string getUrl { get; private set; }
            /// <summary>
            /// 微信授权回调地址
            /// </summary>
            public string wxAuthUrl { get; private set; }
            /// <summary>
            /// 微信服务商授权回调地址
            /// </summary>
            public string wxAuthBase { get; private set; }
            /// <summary>
            /// 应用名称
            /// </summary>
            public string appName { get; private set; }
            /// <summary>
            /// 应用Logo
            /// </summary>
            public string logo { get; private set; }

            public static HostConfig getHost(System.Web.HttpRequestBase Request, string actionName = null, string controllerName = null, object routeValues = null)
            {
                var config = new HostConfig();
                config.reqHost = Request.Url.Host.ToLower();
                config.reqUrl = Request.Url.xTrim();
                config.appName = "互学通";                

                switch (config.reqHost)
                {
                    case "dev.kmhjt.com":
                    case "hxt.dev.kmhjt.com":
                        config.rawHost = "dev.kmhjt.com";
                        config.rawUrl = config.reqUrl.Replace("/hxt.dev.kmhjt.com:6151/", "/dev.kmhjt.com/").Replace("/hxt.dev.kmhjt.com/", "/dev.kmhjt.com/");
                        config.appId = 0;
                        config.wxAuthUrl = config.wxAuthBase = config.getAbsoluteUrl(Request, "wechatAuthCode", "weixin", null);
                        
                        break;
                    case "dev2.kmhjt.com":
                    case "hxt2.dev.kmhjt.com":
                        config.rawHost = "dev2.kmhjt.com";
                        config.rawUrl = config.reqUrl.Replace("/hxt2.dev.kmhjt.com:6151/", "/dev2.kmhjt.com/").Replace("/hxt2.dev.kmhjt.com/", "/dev2.kmhjt.com/");
                        config.appId = 11;
                        config.wxAuthUrl = config.getAbsoluteUrl(Request, "wechatAuthCode", "weixin", null);
                        config.wxAuthBase = config.wxAuthUrl.Replace(config.rawHost, "dev.kmhjt.com");
                        config.appName = "知新云校";
                        break;
                    case "www.kmhjt.com":
                        config.rawHost = "www.kmhjt.com";
                        config.rawUrl = config.reqUrl;
                        config.appId = 0;
                        config.wxAuthUrl = config.wxAuthBase = config.getAbsoluteUrl(Request, "wechatAuthCode", "weixin", null);
                        break;
                    default:
                        config.rawHost = config.reqHost;
                        config.rawUrl = config.reqUrl;
                        config.appId = 0;
                        config.wxAuthUrl = config.wxAuthBase = config.getAbsoluteUrl(Request, "wechatAuthCode", "weixin", null);
                        break;
                }

                if (!string.IsNullOrWhiteSpace(actionName) && !string.IsNullOrWhiteSpace(controllerName))
                {
                    config.getUrl = config.getAbsoluteUrl(Request, actionName, controllerName, routeValues);
                }

                return config;
            }

            public string getAbsoluteUrl(System.Web.HttpRequestBase Request, string actionName, string controllerName, object routeValues = null)
            {
                var urlHelper = new System.Web.Mvc.UrlHelper(Request.RequestContext);
                return urlHelper.Action(actionName, controllerName, new System.Web.Routing.RouteValueDictionary(routeValues), "http", this.rawHost);
            }

            public string getAbsoluteUrl(System.Web.HttpRequestBase Request, string contentPath, bool absolute = false)
            {
                var urlHelper = new System.Web.Mvc.UrlHelper(Request.RequestContext);
                return "http://" + this.rawHost + (absolute ? contentPath : urlHelper.Content(contentPath));
            }
        }
        #endregion

        #region cookie

        public class appCookie
        {
            public appCookie()
            {
                this.pubId = -1;
            }

            /// <summary>
            /// 1：微信服务号；3：微信企业号；4：网站；5：后台；7：微信小程序；8：微信小程序WEB页面
            /// </summary>
            public int app { get; set; }
            /// <summary>
            /// <para>app = 4 | 5：登录key</para>
            /// <para>app = 1：公众号openid</para>
            /// <para>app = 3：企业号userid</para>
            /// <para>app = 7 | 8：小程序openid</para>
            /// </summary>
            public string key { get; set; }
            /// <summary>
            /// app = 1 | 3 有效，key对应的公众号ID
            /// </summary>
            public int pubId { get; set; }
            /// <summary>
            /// 主关联ID
            /// </summary>
            public long mainId { get; set; }
            /// <summary>
            /// app = 3 时有效，企业号openid
            /// </summary>
            public string qyOpenId { get; set; }
            /// <summary>
            /// 服务商对应公众号的openid
            /// </summary>
            public string baseOpenId { get; set; }

            public int uiId { get; set; }
            public int uiType { get; set; }
            public long exp { get; set; }

            [Xcode.Json.JsonIgnore]
            public string error { get; private set; }
            [Xcode.Json.JsonIgnore]
            public string page { get; set; }

            public appCookie SetError(string error)
            {
                this.error = error;
                return this;
            }
        }

        public static string v3CookieName(YYS.ReqFromType reqType, HttpContextBase context)
        {
            var str = DefaultValue.EnvironmentFlag == 0 ? "www."
                    : DefaultValue.EnvironmentFlag == 2 ? "test."
                    : "dev.";
            if (DefaultValue.IsTest) str = "local." + str;
            str = str + "kmhjt.com";
            if (reqType == YYS.ReqFromType.WeiXin)
            {
                var config = v3Auth.HostConfig.getHost(context.Request);
                str = config.appId + "_" + str;
            }
            else if (reqType == YYS.ReqFromType.WeixinQiye)
            {
                str = "1_" + str;
            }
            return str;
        }

        string cookieName
        {
            get
            {
                return v3Auth.v3CookieName(this.reqType, this.HttpContext);
            }
        }

        internal static void v3SetCookie(YYS.ReqFromType reqType, HttpContextBase context, appCookie kie)
        {
            var cookieName = v3Auth.v3CookieName(reqType, context);
            var cookie = new HttpCookie(cookieName);
            cookie.Value = kie.xConvertObjectToJson(false).xAESEncrypt(DefaultValue.EnvAESKey).xToSafeBase64();
            cookie.Path = "/";
            cookie.HttpOnly = true;
            if (reqType == YYS.ReqFromType.WeiXin || reqType == YYS.ReqFromType.WeixinQiye)
            {
                cookie.Expires = DateTime.Now.AddMonths(1);
            }
            context.Response.Cookies.Add(cookie);
        }

        public void setCookie(appCookie kie)
        {
            v3Auth.v3SetCookie(this.reqType, this.HttpContext, kie);
        }

        internal static appCookie v3GetCookie(YYS.ReqFromType reqType, HttpContextBase context)
        {
            var kie = new appCookie();
            var cookieName = v3Auth.v3CookieName(reqType, context);
            var cookie = context.Request.Cookies[cookieName];
            if (cookie == null) return kie.SetError("会话已过期.Authorization.KIE");
            try
            {
                kie = cookie.Value.xTrim().xFromSafeBase64().xAESDecrypt(DefaultValue.EnvAESKey).xConvertJsonToObject<appCookie>();
                if (kie == null)
                {
                    kie = new appCookie();
                    return kie.SetError("会话已过期.Authorization.NULL");
                }
            }
            catch
            {
                return kie.SetError("会话已过期.Authorization.CATCH");
            }
            return kie;
        }

        appCookie getCookie()
        {
            return v3Auth.v3GetCookie(this.reqType, this.HttpContext);
        }


        internal static Xcode.ResultObj<string> v3Login(HttpContextBase context, appCookie kie)
        {
            var data = new Xcode.ResultObj<string>();
            if (context == null || kie == null) return data.SetError("参数不能为空");

            if (kie.uiId <= 0) return data.SetError("参数不正确.uiId");
            if (string.IsNullOrWhiteSpace(kie.page)) return data.SetError("参数不正确.page");

            YYS.ReqFromType reqType = YYS.ReqFromType.Default;
            if (Enum.IsDefined(typeof(YYS.ReqFromType), kie.app))
            {
                reqType = (YYS.ReqFromType)kie.app;
            }
            if (!reqType.xRange(YYS.ReqFromType.Web, YYS.ReqFromType.Background, YYS.ReqFromType.WeiXinMini_Web))
            {
                return data.SetError("参数错误.app");
            }

            var urlHelper = new UrlHelper(context.Request.RequestContext);

            if (reqType.xRange(YYS.ReqFromType.Web, YYS.ReqFromType.Background))
            {
                var nowKie = v3Auth.v3GetCookie(reqType, context);
                kie.uiType = -1;
                kie.key = nowKie.error == null ? nowKie.key : Xcode.API.xGuid();
                var ip = Helper.getRemoteIp();
                var key = Xcode.API.xGuid();
                var realKey = (key + kie.key).xEncryptMd5();

                var db = Helper.GETdbHelper(YYS.dbHelper.EnumServer.Local, YYS.dbHelper.ExecType.IfRead, "HXT_SYS");
                if (reqType == YYS.ReqFromType.Background)
                {
                    db.CmdText = @"
INSERT INTO HXT_SYS.dbo.T_O_OperatorOnline (FsOO_Key, FsOO_RealKey, FnOI_ID, FnBSI_ID, FsOO_IP, FdOO_FirstTime, FdOO_LastTime, FnOO_App, FnOO_UserType, FnOO_MainID)
OUTPUT Inserted.FnOO_UserType
SELECT @FsOO_Key, @FsOO_RealKey, FnOI_ID, 0, @FsOO_IP, GETDATE(), @FdOO_LastTime, 5, 3, @FnOO_MainID
FROM HXT_SYS.dbo.T_S_OperatorInfo WITH(NOLOCK)
WHERE FnOI_ID = @uiId
	AND FnOI_Status < 2
";
                }
                else
                {
                    db.CmdText = @"
INSERT INTO HXT_SYS.dbo.T_O_OperatorOnline (FsOO_Key, FsOO_RealKey, FnOI_ID, FnBSI_ID, FsOO_IP, FdOO_FirstTime, FdOO_LastTime, FnOO_App, FnOO_UserType, FnOO_MainID)
OUTPUT Inserted.FnOO_UserType
SELECT @FsOO_Key, @FsOO_RealKey, FnUI_ID, FnBSI_ID, @FsOO_IP, GETDATE(), @FdOO_LastTime, 4, FnUI_Type, @FnOO_MainID
FROM HXT_BASE.dbo.THXTB_BX_UserInfo WITH(NOLOCK)
WHERE FnUI_ID = @uiId
";
                }
                db.AddParameter("@uiId", System.Data.SqlDbType.Int, kie.uiId);
                db.AddParameter("@FsOO_Key", System.Data.SqlDbType.VarChar, key, 64);
                db.AddParameter("@FsOO_RealKey", System.Data.SqlDbType.VarChar, realKey, 64);
                db.AddParameter("@FdOO_LastTime", System.Data.SqlDbType.DateTime, DateTime.Now.AddMinutes(90));
                db.AddParameter("@FsOO_IP", System.Data.SqlDbType.VarChar, ip, 32);
                db.AddParameter("@FnOO_MainID", System.Data.SqlDbType.BigInt, kie.mainId);
                db.FuncRead = sdr =>
                {
                    kie.uiType = sdr["FnOO_UserType"].xTo<short>();
                };
                db.Exec();
                if (db.HasError) return data.SetError(db.Error);
                if (kie.uiType == -1) return data.SetError("未查询到用户信息");
                kie.exp = DateTime.Now.AddMinutes(90).xToTimeStamp();

                v3Auth.v3SetCookie(reqType, context, kie);

                data.Obj = urlHelper.Content("~/") + kie.page.xTrim().Replace("{key}", key);
            }
            else
            {

            }
            return data;
        }


        public static void logoff(HttpContextBase context, string key)
        {
            var kie = v3Auth.v3GetCookie(YYS.ReqFromType.Web, context);
            var realKey = "";
            if (kie.error == null) realKey = (kie.key + key).xEncryptMd5();

            var db = Helper.GETdbHelper(dbHelper.EnumServer.Local, dbHelper.ExecType.Exec, "HXT_SYS");
            db.CmdText = "UPDATE HXT_SYS.dbo.T_O_OperatorOnline SET FdOO_LastTime = GETDATE() WHERE FsOO_Key = @FsOO_Key OR FsOO_RealKey = @FsOO_RealKey";
            db.AddParameter("@FsOO_Key", System.Data.SqlDbType.VarChar, key, 64);
            db.AddParameter("@FsOO_RealKey", System.Data.SqlDbType.VarChar, realKey, 64);
            db.Exec();
        }

        #endregion
        //private static MemoryCache cache = MemoryCache.Default;
        public void OnAuthorization()
        {
            //var openId = cache.Get("openId");
            //if (openId == null) { NotValid("会话已过期.Authorization.KEY"); }
            //else
            //{
            //    TimeSpan slidingExpirationTime = TimeSpan.FromMinutes(30); // 例如30分钟
            //    CacheHelper.AddLogin("openId", openId.ToString(), DateTimeOffset.UtcNow.Add(slidingExpirationTime));
            //};
            var openId = Request.Form["key"].xTrim();
            var reqType = Request.Form["reqType"].xTo<short>();
            if (reqType == 0)//网页
            {
                var currentUser = YYS.Controllers.pcController.v3getCurrentUser(openId);
                if (currentUser.HasError)
                {
                    NotValid(currentUser.Error);
                    return;
                };
                if (currentUser.Obj == null)
                {
                    NotValid("会话已过期.Authorization.KEY" + currentUser.Obj);
                    return;
                };
                currentUser.Obj.appId = reqType;
                if (this.accessId != null)
                {
                    if (!currentUser.Obj.cando(this.accessId))
                    {
                        NotValid("操作未经授权");
                        return;
                    }
                }
                this.AuthorizationContext.RouteData.DataTokens["__unifiedUser"] = currentUser.Obj;
            };
            if (reqType == 1|| reqType == 2)//1小程序 2公众号
            {
                var currentUser = YYS.Controllers.pcController.v3getCurrentUser(openId, reqType);
                if (currentUser.HasError)
                {
                    NotValid(currentUser.Error);
                    return;
                };
                if (currentUser.Obj == null)
                {
                    NotValid("会话已过期.Authorization.KEY" + currentUser.Obj);
                    return;
                };
                currentUser.Obj.appId = reqType;
                if (this.accessId != null)
                {
                    if (!currentUser.Obj.cando(this.accessId))
                    {
                        NotValid("操作未经授权");
                        return;
                    }
                }
                this.AuthorizationContext.RouteData.DataTokens["__unifiedUser"] = currentUser.Obj;
            }
            
        }
    }
}